﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Security;
using System.Collections.Specialized;
using BussinessLogic;
using WebClasses.Security;
using DomainModel;

namespace WebClasses.Helpers
{
    public static class WebUtilities
    {
        #region GetValueFromQueryStringOrFormCollection
        /// <summary>
        /// Get a string value from either query string or form collection as required.
        /// Will trim the value.
        /// If there is no value (or it's empty, after trimming) the default is returned.
        /// If required then an exception is thrown if missing.
        /// </summary>
        /// <param name="collection">collection to use</param>
        /// <param name="name">arg to get</param>
        /// <param name="required">true if required (will throw if not present)</param>
        /// <param name="defaultValue">Default to use if not present</param>
        /// <returns>string value</returns>
        public static string GetParam(NameValueCollection collection, string name, bool required, string defaultValue)
        {
            return WebUtilities.GetParam(collection, name, required, true, defaultValue);
        }

        /// <summary>
        /// Get a string value from either query string or form collection as required.
        /// Will trim the value.
        /// If there is no value (or it's empty, after trimming) the default is returned.
        /// If required then an exception is thrown if missing.
        /// </summary>
        /// <param name="collection">collection to use</param>
        /// <param name="name">arg to get</param>
        /// <param name="required">true if required (will throw if not present)</param>
        /// <param name="cleaned">true if string will be cleaned</param>
        /// <param name="defaultValue">Default to use if not present</param>
        /// <returns>string value</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "cleaned")]
        public static string GetParam(NameValueCollection collection, string name, bool required, bool cleaned, string defaultValue)
        {
            if (collection == null)
            {
                throw new ArgumentNullException("collection");
            }

            string val = collection[name];

            if (val != null)
            {
                val = val.Trim();
            }

            if (String.IsNullOrEmpty(val))
            {
                if (required)
                {
                    throw new ArgumentException(name);
                }

                return defaultValue;
            }

            return val;
        }

        /// <summary>
        /// Get an long value from the qs/form
        /// If there is no value (or it's empty, after trimming) the default is returned.
        /// If required then an exception is thrown if missing.
        /// </summary>
        /// <param name="collection">Collection to use</param>
        /// <param name="name">arg to get</param>
        /// <param name="required">true if required</param>
        /// <param name="defaultValue">Default to use if not present</param>
        /// <returns>long value</returns>
        public static long GetParam(NameValueCollection collection, string name, bool required, long defaultValue)
        {
            string val = WebUtilities.GetParam(collection, name, required, null);

            if (val == null)
            {
                return defaultValue;
            }

            long result;

            if (!long.TryParse(val, out result))
            {
                throw new ArgumentException(name);
            }

            return result;
        }

        /// <summary>
        /// Get an double value from the qs/form
        /// If there is no value (or it's empty, after trimming) the default is returned.
        /// If required then an exception is thrown if missing.
        /// </summary>
        /// <param name="collection">Collection to use</param>
        /// <param name="name">arg to get</param>
        /// <param name="required">true if required</param>
        /// <param name="defaultValue">Default to use if not present</param>
        /// <returns>double value</returns>
        public static double GetParam(NameValueCollection collection, string name, bool required, double defaultValue)
        {
            string val = WebUtilities.GetParam(collection, name, required, null);

            if (val == null)
            {
                return defaultValue;
            }

            double result;

            if (!double.TryParse(val, out result))
            {
                throw new ArgumentException(name);
            }

            return result;
        }

        /// <summary>
        /// Get an float value from the qs/form
        /// If there is no value (or it's empty, after trimming) the default is returned.
        /// If required then an exception is thrown if missing.
        /// </summary>
        /// <param name="collection">Collection to use</param>
        /// <param name="name">arg to get</param>
        /// <param name="required">true if required</param>
        /// <param name="defaultValue">Default to use if not present</param>
        /// <returns>float value</returns>
        public static float GetParam(NameValueCollection collection, string name, bool required, float defaultValue)
        {
            string val = WebUtilities.GetParam(collection, name, required, null);

            if (val == null)
            {
                return defaultValue;
            }

            float result;

            if (!float.TryParse(val, out result))
            {
                throw new ArgumentException(name);
            }

            return result;
        }

        /// <summary>
        /// Get an int value from the qs/form
        /// If there is no value (or it's empty, after trimming) the default is returned.
        /// If required then an exception is thrown if missing.
        /// </summary>
        /// <param name="collection">Collection to use</param>
        /// <param name="name">arg to get</param>
        /// <param name="required">true if required</param>
        /// <param name="defaultValue">Default to use if not present</param>
        /// <returns>int value</returns>
        public static int GetParam(NameValueCollection collection, string name, bool required, int defaultValue)
        {
            long v = WebUtilities.GetParam(collection, name, required, (long)defaultValue);

            if (v < Int32.MinValue || v > Int32.MaxValue)
            {
                throw new ArgumentException(name);
            }

            return (int)v;
        }

        /// <summary>
        /// Get a bool value from the qs/form
        /// If there is no value (or it's empty, after trimming) the default is returned.
        /// If required then an exception is thrown if missing.
        /// </summary>
        /// <param name="collection">Collection to use</param>
        /// <param name="name">arg to get</param>
        /// <param name="required">true if required</param>
        /// <param name="defaultValue">Default to use if not present</param>
        /// <returns>bool value</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Param")]
        public static bool GetParam(NameValueCollection collection, string name, bool required, bool defaultValue)
        {
            string val = WebUtilities.GetParam(collection, name, required, null);

            if (val == null)
            {
                return defaultValue;
            }

            bool result;

            if (!bool.TryParse(val, out result))
            {
                throw new ArgumentException(name);
            }

            return result;
        }

        /// <summary>
        /// Get a string value from either query string or form collection as required.
        /// Will trim the value.
        /// If there is no value (or it's empty, after trimming) the default is returned.
        /// If required then an exception is thrown if missing.
        /// </summary>
        /// <param name="context">context to get from</param>
        /// <param name="name">arg to get</param>
        /// <param name="queryString">true if from query string, else form</param>
        /// <param name="required">true if required (will throw if not present)</param>
        /// <param name="defaultValue">Default to use if not present</param>
        /// <returns>string value</returns>
        public static string GetParam(HttpContext context, string name, bool queryString, bool required, string defaultValue)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            NameValueCollection collection = queryString ? context.Request.QueryString : context.Request.Form;
            return WebUtilities.GetParam(collection, name, required, defaultValue);
        }

        /// <summary>
        /// Get a string value from either query string or form collection as required.
        /// Will trim the value.
        /// If there is no value (or it's empty, after trimming) the default is returned.
        /// If required then an exception is thrown if missing.
        /// </summary>
        /// <param name="context">context to get from</param>
        /// <param name="name">arg to get</param>
        /// <param name="queryString">true if from query string, else form</param>
        /// <param name="required">true if required (will throw if not present)</param>
        /// <param name="cleanse">true if string needs to be cleansed</param>
        /// <param name="defaultValue">Default to use if not present</param>
        /// <returns>string value</returns>
        public static string GetParam(HttpContext context, string name, bool queryString, bool required, bool cleanse, string defaultValue)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            NameValueCollection collection = queryString ? context.Request.QueryString : context.Request.Form;
            return WebUtilities.GetParam(collection, name, required, cleanse, defaultValue);
        }

        /// <summary>
        /// Get an integer value from the qs/form
        /// If there is no value (or it's empty, after trimming) the default is returned.
        /// If required then an exception is thrown if missing.
        /// </summary>
        /// <param name="context">context to get from</param>
        /// <param name="name">arg to get</param>
        /// <param name="queryString">true if from query string, else form</param>
        /// <param name="required">true if required (will throw if not present)</param>
        /// <param name="defaultValue">Default to use if not present</param>
        /// <returns>int value</returns>
        public static int GetParam(HttpContext context, string name, bool queryString, bool required, int defaultValue)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            NameValueCollection collection = queryString ? context.Request.QueryString : context.Request.Form;
            return WebUtilities.GetParam(collection, name, required, defaultValue);
        }

        /// <summary>
        /// Get a long value from the qs/form
        /// If there is no value (or it's empty, after trimming) the default is returned.
        /// If required then an exception is thrown if missing.
        /// </summary>
        /// <param name="context">context to get from</param>
        /// <param name="name">arg to get</param>
        /// <param name="queryString">true if from query string, else form</param>
        /// <param name="required">true if required (will throw if not present)</param>
        /// <param name="defaultValue">Default to use if not present</param>
        /// <returns>long value</returns>
        public static long GetParam(HttpContext context, string name, bool queryString, bool required, long defaultValue)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            NameValueCollection collection = queryString ? context.Request.QueryString : context.Request.Form;
            return WebUtilities.GetParam(collection, name, required, defaultValue);
        }

        /// <summary>
        /// Get a double value from the qs/form
        /// If there is no value (or it's empty, after trimming) the default is returned.
        /// If required then an exception is thrown if missing.
        /// </summary>
        /// <param name="context">context to get from</param>
        /// <param name="name">arg to get</param>
        /// <param name="queryString">true if from query string, else form</param>
        /// <param name="required">true if required (will throw if not present)</param>
        /// <param name="defaultValue">Default to use if not present</param>
        /// <returns>double value</returns>
        public static double GetParam(HttpContext context, string name, bool queryString, bool required, double defaultValue)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            NameValueCollection collection = queryString ? context.Request.QueryString : context.Request.Form;
            return WebUtilities.GetParam(collection, name, required, defaultValue);
        }

        /// <summary>
        /// Get a float value from the qs/form
        /// If there is no value (or it's empty, after trimming) the default is returned.
        /// If required then an exception is thrown if missing.
        /// </summary>
        /// <param name="context">context to get from</param>
        /// <param name="name">arg to get</param>
        /// <param name="queryString">true if from query string, else form</param>
        /// <param name="required">true if required (will throw if not present)</param>
        /// <param name="defaultValue">Default to use if not present</param>
        /// <returns>float value</returns>
        public static float GetParam(HttpContext context, string name, bool queryString, bool required, float defaultValue)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            NameValueCollection collection = queryString ? context.Request.QueryString : context.Request.Form;
            return WebUtilities.GetParam(collection, name, required, defaultValue);
        }

        /// <summary>
        /// Get an bool value from the qs/form
        /// If there is no value (or it's empty, after trimming) the default is returned.
        /// If required then an exception is thrown if missing.
        /// </summary>
        /// <param name="context">context to get from</param>
        /// <param name="name">arg to get</param>
        /// <param name="queryString">true if from query string, else form</param>
        /// <param name="required">true if required (will throw if not present)</param>
        /// <param name="defaultValue">Default to use if not present</param>
        /// <returns>bool value</returns>
        public static bool GetParam(HttpContext context, string name, bool queryString, bool required, bool defaultValue)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            NameValueCollection collection = queryString ? context.Request.QueryString : context.Request.Form;
            return WebUtilities.GetParam(collection, name, required, defaultValue);
        }
        #endregion

        private const string MIDDLE_TIER_KEY = "MT";

        //用户验证Cookie的Key
        private const string IMAN_AUTH_KEY = "SLYAuth";

        //用户验证码缓存的Key
        private const string IMAN_VERIFICATION_KEY = "SLYVerificationKey";

        /// <summary>
        /// Public static helper for accessing middle tier
        /// Used to share code with HttpHandlers which don't derive from Page.
        /// </summary>
        /// <param name="context">HttpContext</param>
        /// <returns>Middle tier object</returns>
        public static LogicController GetController(HttpContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            LogicController mt = (LogicController)context.Application[MIDDLE_TIER_KEY];

            if (mt == null)
            {
                context.Application.Lock();
                mt = (LogicController)context.Application[MIDDLE_TIER_KEY];

                if (mt == null)
                {
                    mt = LogicController.Instance;
                    context.Application.Add(MIDDLE_TIER_KEY, mt);
                }

                context.Application.UnLock();
            }

            return mt;
        }

        public static AdminIdentity GetIdentity(HttpContext context)
        {
            FormsAuthenticationTicket ticket;

            if (WebUtilities.GetIsAnonymousUser(context, out ticket))
            {
                return AdminIdentity.Anonymous;
            }

            string[] parts = ticket.Name.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);

            return new AdminIdentity(new Admin
            {
                ID = Int32.Parse(parts[0]),
                AdminName = parts[1],
            });
        }

        public static void SetAuthCookie(HttpContext context, int userId, string userName, bool createPersistentCookie)
        {
            WebUtilities.SetAuthCookie(
                context,
                new Admin
                {
                    ID = userId,
                    AdminName = userName,
                },
                createPersistentCookie);
        }

        public static void SetAuthCookie(HttpContext context, Admin user, bool createPersistentCookie)
        {
            HttpCookie cookie = FormsAuthentication.GetAuthCookie(
                user.ID + "|" + user.AdminName,
                createPersistentCookie);

            cookie.Name = IMAN_AUTH_KEY;
            cookie.HttpOnly = false;

            if (createPersistentCookie)
            {
                cookie.Expires = DateTime.Now.AddYears(30);
            }

            context.Response.SetCookie(cookie);

        }

        public static void SignOut(HttpContext context)
        {
            context.Response.Cookies.Set(new HttpCookie(IMAN_AUTH_KEY));
        }

        public static Exception GetLastError(HttpContext context)
        {
            Exception rootEx = context.Server.GetLastError();

            if (rootEx == null)
            {
                return null;
            }

            Exception ex = rootEx;
            while (ex != null && ex is HttpException)
            {
                ex = ex.InnerException;
            }

            return ex;
        }

        private static bool GetIsAnonymousUser(HttpContext context, out FormsAuthenticationTicket ticket)
        {
            string authToken;

            HttpCookie cookie = context.Request.Cookies[IMAN_AUTH_KEY];
            if (cookie != null)
            {
                authToken = cookie.Value;
            }
            else
            {
                authToken = context.Request.Params["auth"];
            }

            if (authToken == null)
            {
                ticket = null;
                return true;
            }

            try
            {
                ticket = FormsAuthentication.Decrypt(authToken);
                return false;
            }
            catch (Exception)
            {
                ticket = null;
                return true;
            }
        }

        public static void SetVerificationKey(HttpContext context, string key)
        {
            context.Response.Cookies.Add(new HttpCookie(IMAN_VERIFICATION_KEY, key));
        }

        public static string GetVerificationKey(HttpContext context)
        {
            HttpCookie cookie = context.Request.Cookies[IMAN_VERIFICATION_KEY];

            return cookie == null ? null : cookie.Value;
        }

    }
}
